﻿#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp> 
#include <opencv2/imgproc.hpp>


cv::Mat __stdcall BGR2Gray(const cv::Mat& srcImg);    // 灰度化
cv::Mat __stdcall Binarize_Otsu(const cv::Mat& img); // 自动二值化

int main(int argc, char const* argv[])
{
    auto img = cv::imread("./../../images/imori.jpg", cv::IMREAD_COLOR);  // 读取方式bgr24 

    cv::Mat grayImage;
    grayImage = BGR2Gray(img);
 
    auto img2 = Binarize_Otsu(grayImage);
    cv::namedWindow("Binarize_Otsu", cv::WINDOW_AUTOSIZE);
    cv::imshow("Binarize_Otsu", img2);

    cv::waitKey(0);
    cv::destroyAllWindows();
    cv::waitKey(0);
    return 0;
}
 
cv::Mat BGR2Gray(const cv::Mat& srcImg)
{
    auto width = srcImg.cols;
    auto height = srcImg.rows;

    cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);  //创建一个8位1通道的图片
 
    // 转换公式 Y= 0.216*R+0.7152*G+0.0722*B;
    for (int y = 0; y < height; y++)  
    {
        for (int x = 0; x < width; x++) 
        {            
            auto&& R = srcImg.at<cv::Vec3b>(y, x)[2];
            auto&& G = srcImg.at<cv::Vec3b>(y, x)[1];
            auto&& B = srcImg.at<cv::Vec3b>(y, x)[0];
            out.at<uint8_t>(y, x) = uint8_t(0.216*R + 0.7152*G + 0.0722*B);
        }
    }

    return out;
}



// Gray -> Binary
cv::Mat Binarize_Otsu(const cv::Mat& gray)
{
  int width = gray.cols;
  int height = gray.rows;

  // determine threshold 
  double w0 = 0, w1 = 0, m0 = 0, m1 = 0;
  double max_sb = 0, sb = 0;
  int th = 0;
  int val;

  // Get threshold  计算阈值
  for (int t = 0; t < 255; t++)
  {
    w0 = 0, w1 = 0, m0 = 0, m1 = 0;
    for (int y = 0; y < height; y++)
    {
      for (int x = 0; x < width; x++)
      {
        val = (int)(gray.at<uchar>(y, x));

        if (val < t)
        {
          w0++;
          m0 += val;
        }
        else
        {
          w1++;
          m1 += val;
        }
      }
    }

    m0 /= w0;
    m1 /= w1;
    w0 /= (height * width);
    w1 /= (height * width);
    sb = w0 * w1 * pow((m0 - m1), 2);
    
    if(sb > max_sb)
    {
      max_sb = sb;
      th = t;
    }
  }

  std::cout << "threshold:" << th << std::endl; 
  // prepare output
  cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);

  // each y, x
  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      // Binarize
      if (gray.at<uchar>(y, x) > th)
      {
        out.at<uchar>(y, x) = 255;
      }
      else
      {
        out.at<uchar>(y, x) = 0;
      }
    
    }
  }

  return out;
}
